package top.eggcode.security.shiro;

import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.mgt.DefaultSessionManager;
import org.apache.shiro.spring.boot.autoconfigure.ShiroAnnotationProcessorAutoConfiguration;
import org.apache.shiro.spring.boot.autoconfigure.ShiroAutoConfiguration;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import top.eggcode.component.redis.RedisUnit;
import top.eggcode.system.client.UserClientService;

import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Title: Shiro 配置类
 * Description: TODO
 * Date: 2021/4/11 21:31
 *
 * @author JiaQi Ding
 * @version 1.0
 */
@AutoConfigureBefore({ShiroAutoConfiguration.class, ShiroAnnotationProcessorAutoConfiguration.class})
@Configuration
public class ShiroConfig {

    @Bean
    public Realm createRealm(UserClientService userClientService) {
        return new UserRealm(userClientService);
    }

    @Bean
    public RedisSessionStore createSessionDao(RedisUnit buffer) {
        RedisSessionStore sessionStore = new RedisSessionStore(buffer);
        return sessionStore;
    }

    /**
     * 安全控制器
     * 这里的 realm 需要懒加载，避免影响 bean 的自动代理
     *
     * @param tokenRealm 访问控制器
     * @return 组件管理器
     */
    @Bean
    public DefaultWebSecurityManager securityManager(
            UserRealm tokenRealm,
            RedisSessionStore sessionStore
    ) {

        // 安全管理器
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        DefaultSessionManager sessionManager = new HttpSessionManager();
        sessionManager.setSessionDAO(sessionStore);
        // 会话时长
        sessionManager.setGlobalSessionTimeout(600000L);
        // Native Mode
        securityManager.setSessionManager(sessionManager);

        // 自定义身份数据源
        securityManager.setRealm(tokenRealm);

        return securityManager;
    }

    /**
     * shiro 的 AOP
     * 让shiro的AOP 失效
     */
    @Bean
    @DependsOn({"lifecycleBeanPostProcessor"})
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        // 强制使用cglib，防止重复代理和可能引起代理出错的问题
        // 因为这里默认使用基于接口的代理，会把其他组件的注解忽略
        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);

        //添加前缀判断 不匹配 任何Advisor
        defaultAdvisorAutoProxyCreator.setUsePrefix(true);
        defaultAdvisorAutoProxyCreator.setAdvisorBeanNamePrefix("_no_advisor");
        return defaultAdvisorAutoProxyCreator;
    }

    /**
     * 请求url管理
     * 需要提前实例化，所以参数 Bean 延迟注入
     *
     * @param securityManager SecurityManager
     * @return 过滤器工厂
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager, UserClientService userClientService) {
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
        // 注入安全控制器
        factoryBean.setSecurityManager(securityManager);

        // 注入自定义过滤器
        Map<String, Filter> filterMap = new HashMap<>(10);
        filterMap.put("session", new SessionFilter());
        factoryBean.setFilters(filterMap);

        Map<String, String> filters = new LinkedHashMap<>();

        filters.put("/**", "session");
        factoryBean.setFilterChainDefinitionMap(filters);
        return factoryBean;
    }

    /**
     * 开启注解通知
     * spring会处理shiro的注解
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }

}
